package com.pingan.haofang.searchcloud.web.index.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.pingan.haofang.framework.common.utils.UUIDUtils;
import com.pingan.haofang.searchcloud.common.constants.WebConstants;
import com.pingan.haofang.searchcloud.common.dto.RPCPageParams;
import com.pingan.haofang.searchcloud.common.rpc.RPCConfigProperties;
import com.pingan.haofang.searchcloud.common.rpc.RPCPage;
import com.pingan.haofang.searchcloud.common.rpc.RPCRequest;
import com.pingan.haofang.searchcloud.common.rpc.RPCResponse;
import com.pingan.haofang.searchcloud.web.index.vo.FullRpcIndexDataSourceVO;
import com.pingan.haofang.searchcloud.web.index.vo.example.IncRpcRequestParam;
import com.pingan.haofang.searchcloud.web.index.vo.test.FullRpcTestRequest;
import com.pingan.haofang.searchcloud.web.index.vo.test.IncRpcTestRequest;
import com.pingan.haofang.searchcloud.web.index.vo.test.RpcTestResponseVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Optional;

/**
 * IndexDatasource 请求测试接口
 *
 * @author zhangbi617
 * @date 14/05/2017
 */
@RestController
@RequestMapping(value = WebConstants.WEB_PREFIX + "/datasource/test")
@Api(tags = "Index.Datasource.Test", description = "索引数据源测试接口")
public class IndexDatasourceTestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexDatasourceTestController.class);

    private static Gson GSON = new GsonBuilder().setPrettyPrinting().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    JsonParser JSON_PARSER = new JsonParser();

    @Autowired
    private RestTemplate restTemplate;

    @ApiOperation("全量导入RPC数据源接口测试")
    @PostMapping("fullRpc/{indexId}")
    public RpcTestResponseVO fullRpcTest(
            @ApiParam(value = "索引ID") @PathVariable("indexId") Long indexId,
            @ApiParam(name = "payload", value = "payload") @RequestBody FullRpcTestRequest fullRpcTestRequest) {

        RPCConfigProperties config = generateRpcConfigProperties(fullRpcTestRequest.getFullDatasourceRpc().getRpcUrl(),
                fullRpcTestRequest.getFullDatasourceRpc().getRpcMethod());

        RPCRequest request = new RPCRequest(UUIDUtils.getUUID(), config.getJsonrpc(),
                fullRpcTestRequest.getFullDatasourceRpc().getRpcMethod(),
                RPCPageParams.of(fullRpcTestRequest.getPageNo(),
                        fullRpcTestRequest.getFullDatasourceRpc().getPageSize(),
                        fullRpcTestRequest.getBefore(),
                        fullRpcTestRequest.getLastMaxId()));

        String requestBody = GSON.toJson(request);
        String responseBody = null;

        try {
            ResponseEntity<String> httpResult = restTemplate.exchange(config.getUrl(), HttpMethod.POST,
                    new HttpEntity<>(requestBody), new ParameterizedTypeReference<String>() {
                    });

            if (httpResult.getStatusCode() != HttpStatus.OK) {
                return RpcTestResponseVO.ofFailed(
                        "http execute faild, httpcode is " + httpResult.getStatusCode(), null);
            }

            responseBody = httpResult.getBody();

            RPCResponse<RPCPage<Object>> result = GSON.fromJson(responseBody,
                    new RPCResponseTypeDefinition<>(Object.class));

            // 以下判断是否正确
            if (result == null || result.getResult() == null) {
                return RpcTestResponseVO.ofFailed("响应不合法" +
                        generateErrorMessage(requestBody, responseBody, null), null);
            }

            return RpcTestResponseVO.ofSuccess(result.getResult());
        } catch (Exception e) {
            return RpcTestResponseVO.ofFailed(generateErrorMessage(requestBody, responseBody, e), e);
        } finally {
            LOGGER.info("Test Request:\n{}", requestBody);
            LOGGER.info("Test Response:\n{}", prettyJsonObject(responseBody));
        }
    }

    @ApiOperation("增量回调接口测试")
    @PostMapping("incRpcCallback/{indexId}")
    public RpcTestResponseVO incRpcTest(
            @ApiParam(value = "索引ID") @PathVariable("indexId") Long indexId,
            @ApiParam(name = "payload", value = "payload") @RequestBody IncRpcTestRequest incRpcTestRequest) {

        RPCConfigProperties config = generateRpcConfigProperties(incRpcTestRequest.getIncDatasourceRpc().getRpcUrl(),
                incRpcTestRequest.getIncDatasourceRpc().getRpcMethod());


        IncRpcRequestParam param = new IncRpcRequestParam();
        param.setPageSize(Optional.ofNullable(incRpcTestRequest.getIncDatasourceRpc()).map(FullRpcIndexDataSourceVO::getPageSize).orElse(100));
        param.setPageNo(Optional.ofNullable(incRpcTestRequest.getPageNo()).orElse(1));
        param.setVersionFrom(Optional.ofNullable(incRpcTestRequest.getVersionFrom()).orElse(0L));
        param.setVersionTo(System.currentTimeMillis());
        RPCRequest request = new RPCRequest(UUIDUtils.getUUID(), config.getJsonrpc(),
                incRpcTestRequest.getIncDatasourceRpc().getRpcMethod(), param);

        String requestBody = GSON.toJson(request);
        String responseBody = null;
        try {
            ResponseEntity<String> httpResult = restTemplate.exchange(config.getUrl(), HttpMethod.POST,
                    new HttpEntity<>(requestBody), new ParameterizedTypeReference<String>() {
                    });
            if (httpResult.getStatusCode() != HttpStatus.OK) {
                return RpcTestResponseVO.ofFailed(
                        "http execute faild, httpcode is " + httpResult.getStatusCode(), null);
            }

            responseBody = httpResult.getBody();

            RPCResponse<Object> response = GSON.fromJson(responseBody,
                    new RPCResponseTypeDefinition<>(Object.class));

            if (response == null || response.getResult() == null || response.getResult().getDatas() == null) {
                return RpcTestResponseVO.ofFailed("响应不合法" +
                        generateErrorMessage(requestBody, responseBody, null), null);
            }

            return RpcTestResponseVO.ofSuccess(response.getResult().getDatas());
        } catch (Exception e) {
            return RpcTestResponseVO.ofFailed(generateErrorMessage(requestBody, responseBody, e), e);
        } finally {
            LOGGER.info("Test Request:\n{}", requestBody);
            LOGGER.info("Test Response:\n{}", prettyJsonObject(responseBody));
        }

    }

    /**
     * 生成错误信息
     *
     * @param requestBody  请求
     * @param responseBody 响应
     * @param e
     * @return
     */
    private String generateErrorMessage(String requestBody, String responseBody, Exception e) {
        // pretty json
        responseBody = prettyJsonObject(responseBody);

        StringBuilder sb = new StringBuilder(e == null ? "" : e.getMessage() + "\n");
        sb.append("REQUEST:\n").append(requestBody).append("\n");
        sb.append("RESPONSE:\n").append(responseBody);
        return sb.toString();
    }

    /**
     * 格式化json
     *
     * @param responseBody
     * @return
     */
    private String prettyJsonObject(String responseBody) {
        try {
            JsonObject responseBodyJson = JSON_PARSER.parse(responseBody).getAsJsonObject();
            responseBody = GSON.toJson(responseBodyJson);
        } catch (Exception ex) {
            // DO NOTHING
        }
        return responseBody;
    }

    /**
     * 创建rpcConfigProperties
     *
     * @param rpcUrl    地址
     * @param rpcMethod 方法
     * @return
     */
    private RPCConfigProperties generateRpcConfigProperties(String rpcUrl, String rpcMethod) {
        RPCConfigProperties config = new RPCConfigProperties();
        config.setUrl(rpcUrl);
        config.setMethod(rpcMethod);
        config.setSuccessCode("0");
        config.setJsonrpc("2.0");
        return config;
    }

    /**
     * RPC响应类型定义
     *
     * @param <T>
     * @author LUYI374
     * @date 2017年3月14日
     * @since 1.0.0
     */
    private static class RPCResponseTypeDefinition<T> implements ParameterizedType {

        private Type clazz;

        public RPCResponseTypeDefinition(Type clazz) {
            this.clazz = clazz;
        }

        @Override
        public Type[] getActualTypeArguments() {
            return new Type[]{clazz};
        }

        @Override
        public Type getRawType() {
            return RPCResponse.class;
        }

        @Override
        public Type getOwnerType() {
            return null;
        }
    }

}
